package com.imxinxin.zookeeper;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.data.Stat;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class TestZoo {

    static List<ACL> ACL_LIST = new ArrayList<>();
    static String CONN = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
    static ZooKeeper zooKeeper1 = null;
    static String LOCK_PATH = "/lock";
    static String LOCK_NODE = LOCK_PATH + "/guid-lock-";


    static {
        ACL_LIST.add(new ACL(31, new Id("world", "anyone")));
        try {
            zooKeeper1 = new ZooKeeper(CONN, 300000, null, false);
            zooKeeper1.create(LOCK_PATH, null, ACL_LIST, CreateMode.PERSISTENT);
        } catch (KeeperException.NodeExistsException nodeExistsException) {
            System.out.println(nodeExistsException.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public static void main(String[] args) throws Exception {
        ZooLock zooLock = new ZooLock(zooKeeper1, LOCK_PATH);
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                try {
                    zooLock.lock();
                    System.out.println(Thread.currentThread().getName() + ",获取锁");
                    Thread.sleep(1000);
                    zooLock.unlock();
                    System.out.println(Thread.currentThread().getName() + ",解锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (KeeperException e) {
                    e.printStackTrace();
                }

            }).start();
        }

    }

    public static class ZooLock {

        private final ZooKeeper zooKeeper;
        private final String lock;
        private ThreadLocal<String> lockSeqThreadLocal;

        public ZooLock(ZooKeeper zooKeeper, String lock) {
            this.zooKeeper = zooKeeper;
            this.lock = lock;
            lockSeqThreadLocal =  new ThreadLocal<>();
        }


        public String createSeq() throws InterruptedException, KeeperException {
            return zooKeeper.create(lock + "/", null, ACL_LIST, CreateMode.EPHEMERAL_SEQUENTIAL);
        }



        public void unlock() throws InterruptedException, KeeperException {
            String lockSeq = lockSeqThreadLocal.get();
            if (lockSeq == null) {
                return;
            }
            Stat stat = zooKeeper.exists(lockSeq, null);

            if (stat == null) {
                lockSeqThreadLocal.remove();
                return;
            }

            zooKeeper.delete(lockSeq, stat.getVersion());
            lockSeqThreadLocal.remove();

        }


        private void lock() throws InterruptedException, KeeperException {
            boolean cr = false;
            String lockSeq = lockSeqThreadLocal.get();
            if (lockSeq == null) {
                cr = true;
                lockSeq = createSeq();
                lockSeqThreadLocal.set(lockSeq);
            }

            try {
                List<String> children = zooKeeper.getChildren(lock, null);
                children.sort(String::compareTo);

                int i = children.indexOf(lockSeq.replaceFirst(lock + "/", ""));

                if (i == 0) {
                    return;
                }
                if (i == -1) {
                    lockSeqThreadLocal.set(createSeq());
                    cr = true;
                    lock();
                    return;
                }


                Thread thread = Thread.currentThread();
                Stat stat = zooKeeper.exists(lock + "/" + children.get(i - 1),
                        event -> UnsafeBean.get().unpark(thread));
                if (stat != null) {
                    UnsafeBean.get().park(false, 0L);
                }

                this.lock();

            } catch (Exception e) {
                if (lockSeq == null) {
                    return;
                }
                if (cr) {
                    Stat stat = zooKeeper.exists(lockSeq, null);
                    if (stat == null) {
                        return;
                    }
                    zooKeeper.delete(lockSeq, stat.getVersion());
                }
                lockSeqThreadLocal.remove();

            }

        }

    }


    public static void getChildren() throws InterruptedException, KeeperException {
        for (int i = 0; i < 10; i++) {
            String c = zooKeeper1.create(LOCK_NODE, new byte[]{(byte) i}, ACL_LIST, CreateMode.EPHEMERAL_SEQUENTIAL);
            System.out.print(c + ",");
        }
        System.out.println("\b");
//        System.out.println(zooKeeper.getChildren(LOCK_NODE, null));
    }


    public static void create_PERSISTENT() throws InterruptedException, KeeperException {
        String c = zooKeeper1.create("/xin", "ccc".getBytes(StandardCharsets.UTF_8), ACL_LIST, CreateMode.PERSISTENT);
        System.out.println(c);
        try {
            c = zooKeeper1.create("/xin", "ccc".getBytes(StandardCharsets.UTF_8), ACL_LIST, CreateMode.PERSISTENT);
        } catch (KeeperException.NodeExistsException e) {
            System.out.println(e.getMessage());
        }

        System.out.println(c);
        System.out.println(zooKeeper1.getEphemerals());

    }


    public static void printField(Object obj) throws IllegalAccessException {

        Field[] fields = obj.getClass().getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            System.out.print(field.getName() + "=" + field.get(obj) + ",");
        }
        System.out.println("\b");

    }
}
